Introduction
Microsoft Graph has a couple of primary ways you can get information about a user in Azure AD. This not only includes things like the user attributes but also groups the user is a member of, access to mail, and etc. Each endpoint does require specific permissions but generally speaking, a user can get the basic information about him/herself via the “me” endpoint.
The “me” endpoint
From our docs page, there is a note that you must have a signed in user in order to use the “me” endpoint:
Calling the /me
endpoint requires a signed-in user and therefore a delegated permission. Application permissions are not supported when using the /me
endpoint.
The reason why you cannot use this when calling it with a token obtained with the client_credentials grant flow is that you do not have a user context. This particular flow only involves application sign-in using its own identity ( client id and client secret or certificate ). As a result, it has an Application token. Here is a sample token that was obtained when I signed in to get the token. You will notice that there is information in this token about the user ( myself ) as well as a scp ( Scopes ) claim:
So, that token was obtained using a delegated flow ( a user signed in ) and has information about the user including the permissions for the audience ( resource – aud claim – not seen here ).
With an application token, which was obtained using the client credentials grant flow, this kind of user information is missing as well as a scp claim. Instead of an scp claim, there will be a “roles” claim for the application permissions obtained:
As you can see with that token, there is absolutely no information about a user. There is information about the application and instead of a scp ( scopes ) claim, there is a “roles” claim, which is the Application permissions that have been configured for this resource ( audience ) on the app registration. So, sending this token to the “me” endpoint in Microsoft Graph cannot work as there is no indication whatsoever as to whom “me” is so you will get an error like this:
error":{ "code":"NoPermissionsInAccessToken", "message":"The token contains no permissions, or permissions can not be understood.", "innerError":{ "oAuthEventOperationId":"48f66de9-xxx-xxxx1-xxxx-399ea6608ec0", "oAuthEventcV":"MkVd0xxxxxvjGFVJkoA.1", "errorUrl":"https://aka.ms/autherrors#error-InvalidGrant", "requestId":"80f8a0e9-xxxx-xxxx-xxxx-88e5d4bb5bb2", "date":"2021-07-30T04:04:38" } } }
How to resolve this issue
When using the client credentials flow to get an application token you must call the “users” endpoint to get information about a user. Things like groups, email, contacts, calendars, etc — anything about a user — that you need to get with an application token must go through the users endpoint. This does require the additional permission of “User.Read.All” since your application will need to look up each user by either User Principal Id ( upn ) or User Object Id to get to the desired endpoint.
Lets take the example of group the user is a member of.
If you want to call https://graph.microsoft.com/v1.0/me/memberOf to get a list of groups a user is a member of but you only have an application token, this obviously would generate an error because, again, there is no user context in your application token. you might see an error like this: 401 Unauthorized or perhaps a “NoPermissionsInAccessToken” error. To fix this using the client credentials application token you received, you would need to make a request like this ( where {upn} should be replaced with the upn of the user ): https://graph.microsoft.com/v1.0/users/{upn}/memberOf
For more information about the differences between delegated and application permissions, please see this post.
Summary
You can only ever call the “me” endpoint in Microsoft Graph when the signed in user has an access token with a scopes ( scp ) claim in it. If it is an application token ( no scopes but only a roles claim and no user context ) then you cannot call the “me” endpoint for anything but instead, must call the “users” endpoint with a specific user upn or id. There is no other way around this.